﻿using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using Pakxel.Data;
using System.Data.Common;
using TeleWin = Telerik.WinControls.UI;

namespace Pakxel.SmartLogistic.Modulos
{
    public partial class CatParent : Telerik.WinControls.UI.RadForm
    {
        MEDataDataContext _DC; //Para conectarse con la DB
        object _Record;//Objeto que almacenara la clase para el acceso a los datos.
        Hashtable _HT;
        AutoCompleteStringCollection _src;

        public CatParent()
        {
            InitializeComponent();
        }

        #region Clases privadas, Eventos y Enumeradores
        public enum Accion
        {
            Insertando = 1,
            Insertado = 2,
            Eliminando = 3,
            Eliminado = 4,
            Cancelando = 5,
            Lectura = 0
        }

        #region Declaracion de Clases privadas
        public class BeforeArgs : EventArgs
        {
            public Accion Operacion { get; private set; }

            public object LlaveUnica { get; private set; }

            public bool Cancelar { get; private set; }

            public BeforeArgs(object PrimaryKey, Accion Action, bool Cancel)
            {
                LlaveUnica = PrimaryKey;
                Operacion = Action;
                Cancelar = Cancel;
            }
        }

        public class AfterArgs : EventArgs
        {
            public Accion Operacion { get; private set; }

            public object Registro { get; private set; }

            public AfterArgs(object Record, Accion Action)
            {
                Registro = Record;
                Operacion = Action;
            }
        }
        #endregion

        #region Delaracion de Eventos
        /// <summary>Delegado para manejar los eventos de inicio de Accion.</summary>
        public delegate void EventBeforeAction(object sender, BeforeArgs e);
        /// <summary>Delegado para manejar los eventos de fin de Accion.</summary>
        public delegate void EventAfterAction(object sender, AfterArgs e);

        /// <summary>Evento para indicar cuando se ha iniciado una instruccion de Insercion</summary>
        public event EventBeforeAction onInserting;
        /// <summary>Evento para indicar cuando se ha finalizado una instruccion de Insercion</summary>
        public event EventAfterAction onInserted;

        /// <summary>Evento para indicar cuando se ha finalizado una instruccion de Actualizacion</summary>
        public event EventAfterAction onUpdated;

        /// <summary>Evento para indicar cuando se ha iniciado una instruccion de Eliminacion</summary>
        public event EventBeforeAction onDeleting;
        /// <summary>Evento para indicar cuando se ha finalizado una instruccion de Eliminacion</summary>
        public event EventAfterAction onDeleted;
        #endregion

        #endregion

        #region Propiedades del Formulario
        /// <summary>Nombre de la tabla de donde se extraeran los datos.</summary>
        [Description("Nombre de la tabla de donde se extraeran los datos."), Category("Enlaces")]
        public string Tabla { get; set; }

        /// <summary>Indica el nombre de la columna sobre la cual se realizaran busquedas automaticas.</summary>
        [Description("Indica el nombre de la columna sobre la cual se realizaran busquedas automaticas."), Category("Enlaces")]
        public string PrimaryField { get; set; }

        /// <summary>Indica el nombre del registro contador de Folios</summary>
        [Description("Indica el nombre del registro contador de Folios."), Category("Enlaces")]
        public string Folio { get; set; }

        /// <summary>Devuelve el estado actual de la operacion.</summary
        public Accion CurrentOperation { get; private set; }

        /// <summary>Indica el campo por el cual validar que no se rpitan los valores y que desplegará busquedas.</summary>
        [Description("Indica el campo por el cual validar que no se rpitan los valores y que desplegará busquedas."), Category("Enlaces")]
        public string UniqueField { get; set; }
        #endregion

        #region Botones de Accion
        private void tsBtnAdd_Click(object sender, EventArgs e)
        {
            CurrentOperation = Accion.Insertando;

            BeforeArgs onAdd = new BeforeArgs(null, CurrentOperation, false);
            if (onInserting != null) onInserting(this, onAdd);

            if (!onAdd.Cancelar)
            {
                _Record = null;
                ClearControls();
                CheckAvailableButtons();

                if (!string.IsNullOrEmpty(Folio))
                    ((TeleWin.RadTextBox)_HT[PrimaryField]).ReadOnly = true;

                statusMsg.Text = "Preparando para Agregar un nuevo elemento.";
            }
        }

        private void tsBtnDel_Click(object sender, EventArgs e)
        {
            CurrentOperation = Accion.Eliminando;
            BeforeArgs onDel = new BeforeArgs(_Record, CurrentOperation, false);
            if (onDeleting != null) onDeleting(this, onDel);

            if (!onDel.Cancelar)
                if (MessageBox.Show("¿Eliminar este registro?", "Eliminar", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    MethodInfo DeleteonSubmit = _DC.GetType().GetProperty(Tabla + "s").PropertyType.GetMethod("DeleteOnSubmit");
                    DeleteonSubmit.Invoke(_DC.GetType().GetProperty(Tabla + "s").GetValue(_DC, null), new object[] { _Record });

                    DbTransaction transaction = null;
                    try
                    {
                        _DC.Connection.Open();
                        using (transaction = _DC.Connection.BeginTransaction(IsolationLevel.Serializable))
                        {
                            _DC.Transaction = transaction;
                            _DC.SubmitChanges();
                            transaction.Commit();
                        }

                        _Record = null;
                        ClearControls();
                        statusMsg.Text = "Registro eliminado.";
                    }
                    catch (Exception _ex)
                    {
                        if (transaction != null) transaction.Rollback();
                        MessageBox.Show("No se pudo eliminar este registro debido a:\n" + _ex.Message, "Eliminar", MessageBoxButtons.OK, MessageBoxIcon.Information);
                        return;
                    }
                    finally { _DC.Connection.Close(); }

                    if (onDeleted != null) onDeleted(this, new AfterArgs(null, CurrentOperation));
                }

            CurrentOperation = Accion.Lectura;
        }

        private void tsBtnSve_Click(object sender, EventArgs e)
        {
            Accion _tmp = CurrentOperation;
            CurrentOperation = Accion.Insertado;

            try
            {
                if (_DC.Connection.State == ConnectionState.Closed)
                    _DC.Connection.Open();

                if (_tmp == Accion.Insertando)
                {

                    using (DbTransaction transaction = _DC.Connection.BeginTransaction(IsolationLevel.Serializable))
                    {
                        try
                        {
                            _DC.Transaction = transaction;
                            
                            if (string.IsNullOrEmpty(Folio) && string.IsNullOrEmpty(((Control)_HT[PrimaryField]).Text))
                                throw new Exception("Debe ingresar un numero o identificador de registro.");
                            else if (!string.IsNullOrEmpty(Folio))
                            {
                                T014 Parametros = (from P in _DC.T014s where P.T014IdParametro.Equals(Folio) select P).SingleOrDefault();
                                Parametros.T014Val1 = (Convert.ToInt32(Parametros.T014Val1) + 1).ToString(); //(onAdd.LlaveUnica ?? Convert.ToInt32(Parametros.T014Val1) + 1).ToString();
                                _DC.SubmitChanges();

                                ((Control)_HT[PrimaryField]).Text = Parametros.T014Val1;
                            }

                            _Record = Activator.CreateInstance(Type.GetType("Pakxel.Data." + Tabla));

                            FillRecord(_Record);

                            MethodInfo InsertonSubmit = _DC.GetType().GetProperty(Tabla + "s").PropertyType.GetMethod("InsertOnSubmit");
                            InsertonSubmit.Invoke(_DC.GetType().GetProperty(Tabla + "s").GetValue(_DC, null), new object[] { _Record });

                            _DC.SubmitChanges();
                            transaction.Commit();                            
                        }
                        catch (Exception _ex)
                        {
                            transaction.Rollback();
                            CurrentOperation = _tmp;

                            MessageBox.Show("No se pudo crear un nuevo registro debido a:\n" + _ex.Message, "Nuevo Registro", MessageBoxButtons.OK, MessageBoxIcon.Error);

                            return;
                        }
                        finally { _DC.Connection.Close(); }

                        ((TeleWin.RadTextBox)_HT[PrimaryField]).ReadOnly = false;
                        CurrentOperation = Accion.Insertado;

                        if (onInserted != null)
                            onInserted(this, new AfterArgs(_Record, CurrentOperation));
                    }
                }
                else //Actualizar el registro actual.
                {
                    FillRecord(_Record);

                    _DC.SubmitChanges();

                    CurrentOperation = Accion.Lectura;
                    if (onUpdated != null)
                        onUpdated(this, new AfterArgs(_Record, CurrentOperation));
                }
            }
            catch {
                CurrentOperation = _tmp;
                MessageBox.Show("No se pudo conectar con la base de datos. Revise su conexion.", "Conexion", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            finally { _DC.Connection.Close(); }

            CurrentOperation = Accion.Lectura;
            CheckAvailableButtons();
            statusMsg.Text = "Los cambios se realizaron correctamente.";
        }

        private void tsBtnCnl_Click(object sender, EventArgs e)
        {
            if (_HT.ContainsKey(UniqueField))
                ((TeleWin.RadTextBox)_HT[UniqueField]).TextBoxElement.TextBoxItem.BackColor = Color.LightYellow;

            if (_Record != null)
                ShowRecord(_Record);
            else
                ClearControls();

            ((TeleWin.RadTextBox)_HT[PrimaryField]).ReadOnly = false;
            CurrentOperation = Accion.Lectura;
            CheckAvailableButtons();
            statusMsg.Text = "Cambios Cancelados";
        }
        #endregion
        #region Procedimientos privados del formulario
        private void CatParent_Load(object sender, EventArgs e)
        {
            if (!DesignMode && !string.IsNullOrEmpty(Tabla))
            {
                if (Type.GetType("Pakxel.Data." + Tabla) != null)
                {
                    _HT = new Hashtable();
                    _DC = new MEDataDataContext(WorkBench.ConnectionString);

                    BindControls(rGBMain);
                    CheckAvailableButtons();

                    Control ctl;
                    if (!string.IsNullOrEmpty(PrimaryField))
                    {
                        ctl = ((Control)_HT[PrimaryField]);
                        ctl.KeyUp += SearchControl_KeyUp;
                        ctl.BackColor = Color.LightYellow;
                    }

                    if (!string.IsNullOrEmpty(UniqueField) && ((Control)_HT[UniqueField]).GetType().Name == "RadTextBox")
                    {
                        ctl = ((Control)_HT[UniqueField]);
                        ((TeleWin.RadTextBox)ctl).TextBoxElement.TextBoxItem.BackColor = Color.LightYellow;
                        ctl.KeyUp += UniqueControl_KeyUp;
                        ctl.LostFocus += UniqueControl_LostFocus;
                        _src = new AutoCompleteStringCollection();// (IList)_DC.GetType().GetProperty(Tabla + "s").GetValue(_DC, null)

                        _src.AddRange((from X in ((System.Data.Linq.ITable)_DC.GetType().GetProperty(Tabla + "s").GetValue(_DC, null)).Cast<object>().ToList()
                                       select X.GetType().GetProperty(UniqueField).GetValue(X, null).ToString().ToUpper()).ToArray());
                        ((TeleWin.RadTextBox)ctl).AutoCompleteCustomSource = _src;
                        ((TeleWin.RadTextBox)ctl).AutoCompleteMode = AutoCompleteMode.Suggest;
                        ((TeleWin.RadTextBox)ctl).AutoCompleteSource = AutoCompleteSource.CustomSource;
                    }
                }
                else
                    throw new NullReferenceException("No se especifico un origen para obtener datos.");
            }
        }

        private void UniqueControl_LostFocus(object sender, EventArgs e)
        { 
        }

        private void UniqueControl_KeyUp(object sender, KeyEventArgs e)
        {
            string _Texto = ((Control)sender).Text.ToUpper();

            if (CurrentOperation != Accion.Lectura)//Insercion de uno Nuevo
            {
                bool Valido = !_src.Contains(_Texto);
                ((TeleWin.RadTextBox)sender).TextBoxElement.TextBoxItem.BackColor = (Valido ? Color.LightYellow : Color.FromArgb(0xF9, 0xBA, 0xBA));
                tsBtnSve.Enabled = Valido;
            }
            else //Buqueda
            {
                if (e.KeyCode == Keys.Enter && !string.IsNullOrEmpty(_Texto))
                {
                    statusMsg.Text = "";

                    object _Busqueda = (from X in ((System.Data.Linq.ITable)_DC.GetType().GetProperty(Tabla + "s").GetValue(_DC, null)).Cast<object>().ToList()
                                        where X.GetType().GetProperty(((Control)sender).Tag.ToString()).GetValue(X, null).ToString().ToUpper().Equals(_Texto)
                                        select X).SingleOrDefault();

                    if (_Busqueda != null)
                    {
                        _Record = _Busqueda;
                        ShowRecord(_Record);
                        CheckAvailableButtons();
                    }
                    else
                        statusMsg.Text = "No se encontro ningun registro.";
                }
                else if(_Record != null)
                {
                    bool Valido = !_src.Contains(_Texto) ||
                        Type.GetType("Pakxel.Data." + Tabla).GetProperty(UniqueField).GetValue(_Record, null).ToString().ToUpper().Equals(_Texto);
                    ((TeleWin.RadTextBox)sender).TextBoxElement.TextBoxItem.BackColor = (Valido ? Color.LightYellow : Color.FromArgb(0xF9, 0xBA, 0xBA));
                    tsBtnSve.Enabled = Valido;
                }
            }

        }

        private void SearchControl_KeyUp(object sender, KeyEventArgs e)
        {
            if (CurrentOperation != Accion.Lectura) return;

            string _Texto = ((Control)sender).Text;
            if (e.KeyCode == Keys.Enter && !string.IsNullOrEmpty(_Texto))
            {
                statusMsg.Text = "";
                object _Busqueda = null;

                try
                {
                    switch (Tabla)
                    {
                        case "T011": _Busqueda = (from Search in _DC.T011s where Search.T011Entity.Equals(Convert.ToInt32(_Texto)) select Search).SingleOrDefault<T011>(); break;
                        case "T010": _Busqueda = (from Search in _DC.T010s where Search.T010NoOperador.Equals(_Texto) select Search).SingleOrDefault<T010>(); break;
                        case "T008": _Busqueda = (from Search in _DC.T008s where Search.T008IdOrigen.Equals(_Texto) select Search).SingleOrDefault<T008>(); break;
                        case "T006": _Busqueda = (from Search in _DC.T006s where Search.T006NoUnidad.Equals(_Texto) select Search).SingleOrDefault<T006>(); break;
                        case "T009": _Busqueda = (from Search in _DC.T009s where Search.T009IdLinea.Equals(_Texto) select Search).SingleOrDefault<T009>(); break;
                        case "T007": _Busqueda = (from Search in _DC.T007s where Search.T007IdRemolque.Equals(_Texto) select Search).SingleOrDefault<T007>(); break;
                        default: return;
                    }

                }
                catch { MainApp.WriteToLog("Conversion invalida."); }

                #region Codigo Linq por investigarle mas....
                /*
            var _Sel = ((Control)sender).Tag.ToString();
            MethodInfo Equals = _Record.GetType().GetProperty(_Sel).ReflectedType.GetMethod("Equals");
            Equals.Invoke(_DC.GetType().GetProperty(Tabla + "s").GetValue(_DC, null), new object[] { _Record });

            System.Collections.Generic.IEnumerable<System.Data.Linq.Table<object>> ps = _DC.GetTable(_Record.GetType()).Cast<System.Data.Linq.Table<object>>();

            //_Busqueda = ps.SingleOrDefault(a => a.ProductID == p.ProductID);

            _Busqueda = (from Search in (_DC.GetType().GetProperty(_Sel + "s").GetValue(_DC, null) as System.Collections.Generic.List<object>)
                            where (bool)Equals.Invoke(Search.GetType().GetProperty(_Sel).GetValue(_DC, null), new object[] { Convert.ToInt32(_Texto) })
                            select Search).SingleOrDefault<object>();
            */
                #endregion

                if (_Busqueda != null)
                {
                    _Record = _Busqueda;
                    ShowRecord(_Record);
                    CheckAvailableButtons();
                }
                else
                    statusMsg.Text = "No se encontro ningun registro.";
            }
        }

        private void CatParent_FormClosing(object sender, FormClosingEventArgs e)
        {
            e.Cancel = (CurrentOperation != Accion.Lectura);
        }

        /// <summary>Valida la cantidad de registros para habilitar ciertos botones</summary>
        /// <param name="Edicion">Indica si el chequeo es para Edicion</param>
        private void CheckAvailableButtons()
        {
            tsBtnDel.Enabled = (CurrentOperation == Accion.Lectura && _Record != null);
            tsBtnAdd.Enabled = (CurrentOperation == Accion.Lectura);
            tsBtnSve.Enabled = ((CurrentOperation == Accion.Lectura && _Record != null) || CurrentOperation == Accion.Insertando);
            tsBtnCnl.Enabled = (CurrentOperation != Accion.Lectura);

            statusMsg.Text = "";
        }

        /// <summary>Recorre el formulario para detectar los controles que desplegaran los datos.</summary>
        /// <param name="control">Control Padre.</param>
        private void BindControls(Control control)
        {
            foreach (Control Ctl in control.Controls)
            {
                if (!string.IsNullOrEmpty((string)Ctl.Tag))
                    _HT.Add(Ctl.Tag, Ctl);
                else if (Ctl.Controls.Count > 0)
                    BindControls(Ctl);
            }
        }

        private void FillRecord(object Registro)
        {
            Type _Table = Type.GetType("Pakxel.Data." + Tabla);
            PropertyInfo _pInfo;

            Control Ctl;
            foreach(DictionaryEntry val in _HT)
            {
                Ctl = (Control)_HT[val.Key];
                switch (Ctl.GetType().Name)
                {
                    case "RadDateTimePicker":
                        _Table.GetProperty((string)val.Key).SetValue(Registro, ((TeleWin.RadDateTimePicker)Ctl).Value, null); break;
                    case "RadSpinEditor":
                        _Table.GetProperty((string)val.Key).SetValue(Registro, ((TeleWin.RadSpinEditor)Ctl).Value, null); break;
                    case "RadMultiColumnComboBox":
                        TeleWin.RadMultiColumnComboBox rMCCB = (TeleWin.RadMultiColumnComboBox)Ctl;                        
                        _Table.GetProperty((string)val.Key).SetValue(Registro, rMCCB.EditorControl.Rows[rMCCB.SelectedIndex].Cells[1].Value, null); break;
                    case "RadDropDownList":
                        _Table.GetProperty((string)val.Key).SetValue(Registro, ((TeleWin.RadDropDownList)Ctl).SelectedValue, null); break;
                    case "RadRadioButton":
                        _Table.GetProperty((string)val.Key).SetValue(Registro, ((TeleWin.RadRadioButton)Ctl).IsChecked, null); break;
                    case "RadTextBox":
                        _pInfo = _Table.GetProperty((string)val.Key);
                        switch (_pInfo.PropertyType.Name)
                        {
                            case "Int32": _pInfo.SetValue(Registro, Convert.ToInt32(((TeleWin.RadTextBox)Ctl).Text), null); break;
                            case "Char": _pInfo.SetValue(Registro, Convert.ToChar(((TeleWin.RadTextBox)Ctl).Text), null); break;
                            default: _pInfo.SetValue(Registro, ((TeleWin.RadTextBox)Ctl).Text, null); break;
                        }
                        break;
                    case "RadMaskedEditBox":
                        _pInfo = _Table.GetProperty((string)val.Key);
                        switch (_pInfo.PropertyType.Name)
                        {
                            case "Int32": _pInfo.SetValue(Registro, Convert.ToInt32(((TeleWin.RadMaskedEditBox)Ctl).Value), null); break;
                            case "Decimal": _pInfo.SetValue(Registro, Convert.ToDecimal(((TeleWin.RadMaskedEditBox)Ctl).Text), null); break;
                            default: _pInfo.SetValue(Registro, ((TeleWin.RadTextBox)Ctl).Text, null); break;
                        }
                        break;
                }                
            }
        }

        private void ShowRecord(object Registro)
        {
            Type _Table = Type.GetType("Pakxel.Data." + Tabla);

            Control Ctl;
            foreach (DictionaryEntry val in _HT)
            {
                Ctl = (Control)_HT[val.Key];
                switch (Ctl.GetType().Name)
                {
                    case "RadDateTimePicker":
                        ((TeleWin.RadDateTimePicker)Ctl).Value = (DateTime)_Table.GetProperty((string)val.Key).GetValue(Registro, null); break;
                    case "RadSpinEditor":
                        ((TeleWin.RadSpinEditor)Ctl).Value = (decimal)_Table.GetProperty((string)val.Key).GetValue(Registro, null); break;
                    case "RadMultiColumnComboBox":
                        TeleWin.RadMultiColumnComboBox rMCCB = (TeleWin.RadMultiColumnComboBox)Ctl;
                        rMCCB.SelectedValue = _Table.GetProperty((string)val.Key).GetValue(Registro, null); break;
                    case "RadDropDownList":
                        ((TeleWin.RadDropDownList)Ctl).SelectedValue = _Table.GetProperty((string)val.Key).GetValue(Registro, null); break;
                    case "RadRadioButton":
                        ((TeleWin.RadRadioButton)Ctl).IsChecked = (bool)_Table.GetProperty((string)val.Key).GetValue(Registro, null); break;
                    case "RadTextBox":
                        ((TeleWin.RadTextBox)Ctl).Text = _Table.GetProperty((string)val.Key).GetValue(Registro, null).ToString(); break;
                    case "RadMaskedEditBox":
                        ((TeleWin.RadMaskedEditBox)Ctl).Value = _Table.GetProperty((string)val.Key).GetValue(Registro, null).ToString(); break;
                }
            }
        }

        private void ClearControls()
        {
            Control Ctl;
            foreach (DictionaryEntry val in _HT)
            {
                Ctl = (Control)_HT[val.Key];
                switch (Ctl.GetType().Name)
                {
                    case "RadDateTimePicker":
                        ((TeleWin.RadDateTimePicker)Ctl).Value = DateTime.Now; break;
                    case "RadSpinEditor":
                        ((TeleWin.RadSpinEditor)Ctl).Value = ((TeleWin.RadSpinEditor)Ctl).Minimum; break;
                    case "RadMultiColumnComboBox":
                        ((TeleWin.RadMultiColumnComboBox)Ctl).SelectedIndex = -1; break;
                    case "RadDropDownList":
                        ((TeleWin.RadDropDownList)Ctl).SelectedIndex = -1; break;
                    case "RadRadioButton":
                        ((TeleWin.RadRadioButton)Ctl).IsChecked = false; break;
                    case "RadTextBox":
                        ((TeleWin.RadTextBox)Ctl).Text = ""; break;
                    case "RadMaskedEditBox":
                        ((TeleWin.RadMaskedEditBox)Ctl).Text = ""; break;
                }
            }
        }
        #endregion

    }
}
